#include "includes.h"
#include "ioapi.h"
#include "upgrade_img.h"
#include <sys/time.h>
static unsigned int crc_tb[256]=
{0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
unsigned short get_crc(char * data,unsigned size)
{
	unsigned short CRC = 0;
	int i;
	unsigned char high8b;
	unsigned char c=0;
	for(i=0;i<size;i++)
	{
		high8b=CRC>>8;
		CRC<<=8;
		c=data[i];
		CRC^=crc_tb[high8b^c];
		
	}
	return CRC;
}

unsigned get_checksum(unsigned * data,unsigned size)
{
	unsigned checksum=0;
	int i ;
	for(i=0;i<size;i++)
	{
		checksum^=data[i];
	}
	return checksum;
}


static int upgrade_write(int fd,int lba,char * buff)
{
	int req,status;
	
	blkdev_request  arg_nand;
	req = BLKIO_REQUEST;
	arg_nand.start = lba;
	arg_nand.count = 1;
	arg_nand.bufs[0].buffer = buff;
	arg_nand.req = BLKDEV_REQ_WRITE;
	status = ioctl(fd,req,(void *)&arg_nand);
	return status;
}
static int upgrade_clear(int fd,int lba)
{
	int req,status;
	
	blkdev_request  arg_nand;
	req = BLKIO_NAND_CLEAR;
	arg_nand.start = lba;
	arg_nand.count = 1;
	arg_nand.bufs[0].buffer = 0;
	arg_nand.req = BLKDEV_REQ_READ;
	status = ioctl(fd,req,(void *)&arg_nand);
	return status;
}
static UpgradeProcessInfo upgrade_check_file(SystemDescription * desc,const char * source,AML_UPGRADE_HEADER ** out_header,unsigned para)
{
	int fd_src=-1;
	AML_UPGRADE_HEADER * head;
	head=(AML_UPGRADE_HEADER *)AVMem_malloc(512);
	if(!head)
	{
		(*desc->callback)(UPGRADE_Malloc_Error,0,para);
		return UPGRADE_Malloc_Error;
	}
	fd_src=open(source,O_RDONLY);
	if(fd_src<0)
	{
		desc->callback(UPGRADE_OPEN_ERROR,0,para);
		AVMem_free(head);
		return UPGRADE_OPEN_ERROR;
	}
	read(fd_src,head,512);
	if(head->magic.head_size>512)
	{
		AVMem_free(head);
		head=(AML_UPGRADE_HEADER *)AVMem_malloc(head->magic.head_size);
		if(!head)
		{
			desc->callback(UPGRADE_Malloc_Error,0,para);
			close(fd_src);
			return UPGRADE_Malloc_Error;
		}
		lseek(fd_src,0,SEEK_SET);
		read(fd_src,head,head->magic.head_size);
	}
	
	AML_UPGRADE_SEG * seg=(AML_UPGRADE_SEG *)&head[1];
	unsigned char * temp=(unsigned char *)&head[0];
	if(head->magic.head_crc!=get_crc((char *)(temp+sizeof(head->magic)),head->magic.head_size-sizeof(head->magic)))
	{
		desc->callback(UPGRADE_CRC_ERROR,0,para);
		close(fd_src);
		AVMem_free(head);
		return UPGRADE_CRC_ERROR;
	}
	int i,j,k,l,n,read_size;
	l=0;
	unsigned char * buffer;
	buffer=(unsigned char *)AVMem_malloc(2048);
	if(!buffer)
	{
		close(fd_src);
		AVMem_free(head);
		desc->callback(UPGRADE_Malloc_Error,0,para);
		return UPGRADE_Malloc_Error;
	}
	for(i=0;i<head->seg_num;i++)
	{
		unsigned slot=(seg[i].ident.slot);
		if(slot>desc->device_num)
		{
			close(fd_src);
			AVMem_free(head);
			AVMem_free(buffer);
			desc->callback(UPGRADE_CHECK_ERROR,0,para);
			return UPGRADE_CHECK_ERROR;
		}
		lseek(fd_src,seg[i].offset,SEEK_SET);
		unsigned check_sum=0;
		for(n=0;n<seg[i].size;n+=2048)
		{
			read_size=(seg[i].size-n)>2048?2048:(seg[i].size-n);
			read(fd_src,buffer,read_size);
			check_sum^=get_checksum((unsigned*)buffer,read_size>>2);
		}
		if(check_sum!=seg[i].check_sum)
		{
			close(fd_src);
			AVMem_free(head);
			AVMem_free(buffer);
			desc->callback(UPGRADE_CHECK_ERROR,0,para);
			return UPGRADE_CHECK_ERROR;
		}
	}
	
	close(fd_src);
//	AVMem_free(head);
	*out_header=head;
	AVMem_free(buffer);
	return UPGRADE_INIT;
}
static UpgradeProcessInfo upgrade_file(SystemDescription * desc,const char *source,AML_UPGRADE_HEADER * head,unsigned para)
{
	int fd_src=-1;
	
	
	fd_src=open(source,O_RDONLY);
	if(fd_src<0)
	{
		desc->callback(UPGRADE_OPEN_ERROR,0,para);
		return UPGRADE_OPEN_ERROR;
	}
	
	AML_UPGRADE_SEG * seg=(AML_UPGRADE_SEG *)&head[1];
	int i,j,k,l,n,read_size;
	l=0;
	unsigned char * buffer;
	buffer=(unsigned char *)AVMem_malloc(2048);
	if(!buffer)
	{
		close(fd_src);
		desc->callback(UPGRADE_Malloc_Error,0,para);
		return UPGRADE_Malloc_Error;
	}
	unsigned percent=0xff;
	unsigned total_size=0;
	for(i=0;i<(head->seg_num);i++)
	{
		total_size+=seg[i].size;
	}
	unsigned current_size=0;
	for(i=0;i<(head->seg_num);i++)
	{
		int fd_target=-1;
		unsigned slot=(seg[i].ident.slot);
		fd_target=open((const char *)desc->device[slot].device,O_RDWR);
		unsigned blks;
		blks = ioctl(fd_target,BLKIO_GET_PAGES_IN_BLOCK,(void *)0,para);
		if(blks==0)
		{
			close(fd_target);
			continue;
		}
		unsigned sector;
		sector = ioctl(fd_target,BLKIO_GETBLKSIZE,(void *)0,para);
		if(fd_target<0)
		{
			close(fd_src);
			AVMem_free(buffer);
			desc->callback(UPGRADE_OPEN_ERROR,0,para);
			return UPGRADE_OPEN_ERROR;
		}else{
//			lseek(fd_target,desc->device[j].offset,SEEK_SET);
		}
	
		lseek(fd_src,seg[i].offset,SEEK_SET);
		
		for(n=0;n<seg[i].size;n+=sector)
		{
			read_size=(seg[i].size-n)>sector?sector:(seg[i].size-n);
			read(fd_src,buffer,read_size);			
			//read(fd_target,buffer_1,2048);
			if(n%(sector*blks)==0)
			{
				upgrade_clear(fd_target,n/sector);
			}
			
			//read(fd_target,buffer_1,2048);
			upgrade_write(fd_target,n/sector,buffer);
			//read(fd_target,buffer_1,2048);
			current_size+=read_size;
			unsigned p1=(current_size*100)/total_size;
			if(p1!=percent)
			{
				percent=p1;
				desc->callback(UPGRADING,percent,para);
			}
		}
		close(fd_target);
	}
	
	close(fd_src);
	AVMem_free(head);
	AVMem_free(buffer);
	desc->callback(UPGRADE_FINISHED,0,para);
	return UPGRADE_FINISHED;
}
UpgradeProcessInfo upgrade_image_file(SystemDescription * desc,const char *source,unsigned para)
{
	UpgradeProcessInfo ret;
	AML_UPGRADE_HEADER * head;
	ret=upgrade_check_file(desc,source,&head,para);
	if(ret!=UPGRADE_INIT)
		return ret;
	desc->callback(UPGRADE_CHECK_OK,0,para);
	ret=upgrade_file(desc,source,head,para);
	AVMem_free(head);
	return ret;
}

UpgradeProcessInfo get_upgrade_file_info(const char * source,char * ver_str,int ver_len,char * create_date,int date_len)
{
	int fd_src=-1;
	AML_UPGRADE_HEADER * head;
	head=(AML_UPGRADE_HEADER *)AVMem_malloc(512);
	if(!head)
	{
		return UPGRADE_Malloc_Error;
	}
	fd_src=open(source,O_RDONLY);
	if(fd_src<0)
	{
		
		AVMem_free(head);
		return UPGRADE_OPEN_ERROR;
	}
	read(fd_src,head,512);
	if(head->magic.head_size>512)
	{
		AVMem_free(head);
		head=(AML_UPGRADE_HEADER *)AVMem_malloc(head->magic.head_size);
		if(!head)
		{
			close(fd_src);
			return UPGRADE_Malloc_Error;
		}
		lseek(fd_src,0,SEEK_SET);
		read(fd_src,head,head->magic.head_size);
	}
	
	AML_UPGRADE_SEG * seg=(AML_UPGRADE_SEG *)&head[1];
	unsigned char * temp=(unsigned char *)&head[0];
	if(head->magic.head_crc!=get_crc((unsigned char *)(temp+sizeof(head->magic)),head->magic.head_size-sizeof(head->magic)))
	{
		close(fd_src);
		AVMem_free(head);
		return UPGRADE_CRC_ERROR;
	}
	int i,j,k,l,n,read_size;
	l=0;
	unsigned char * buffer;
	buffer=(unsigned char *)AVMem_malloc(2048);
	if(!buffer)
	{
		close(fd_src);
		AVMem_free(head);
		return UPGRADE_Malloc_Error;
	}
	unsigned slot=0;
	OutIdent *ident_out=&seg[0].ident;
	for(i=0;i<head->seg_num;i++)
	{
		 if(slot<(seg[i].ident.slot));
		 {
			slot=seg[i].ident.slot;
			ident_out=&seg[i].ident;
		 }
	}
	strftime(create_date,date_len,"%Y.%m.%d.%H.%M",gmtime((time_t *)&(ident_out->create_date)));
	snprintf(ver_str,ver_len,"V1.0_r%d",ident_out->software_ver);
	close(fd_src);
	AVMem_free(head);
	
	AVMem_free(buffer);
	return UPGRADE_INIT;
}
extern const OutIdent software_ident_struct;
char version_date[40];
char version_no[40] ;
void set_version_info()
{
	static int a=0;
	if(a)
		return ;
	a=1;
	strftime(version_date,40,"%Y.%m.%d.%H.%M",gmtime((time_t *)&(software_ident_struct.create_date)));
	snprintf(version_no,40,"V1.0_r%d",software_ident_struct.software_ver);
}
